package org.uim.cricket.analyzer.parsers.match.impl;

import java.util.HashMap;
import java.util.ArrayList;

import org.uim.cricket.analyzer.parsers.Plogger;
import org.uim.cricket.analyzer.parsers.aggr.SingleInningParsable;
import org.uim.cricket.analyzer.parsers.match.*;
import org.uim.cricket.analyzer.parsers.misc.SimpleNameParsable;
import org.uim.cricket.entities.aggr.SingleInning;
import org.uim.cricket.entities.match.CricketMatch;
import org.uim.cricket.util.HTMLStringUtils;
import org.uim.cricket.util.MyStringUtils;

public class DefaultCricketMatchParser
        implements CricketMatchParsable {

    private GroundParsable groundParser = null;
    private MatchDatesParsable dateParser = null;
    private MatchIDParsable idParser = null;
    private MatchResultParsable resultParser = null;
    private MatchSpanParsable spanParser = null;
    private DebutsParsable debutParser = null;
    private MatchSeriesParsable seriesParser = null;
    private TossParsable tossParser = null;
    private SeriesResultParsable seriesResultParser = null;
    private SimpleNameParsable simpleNameParser = null;
    private MatchOfficialParsable officialParser = null;
    
    private SingleInningParsable singleInnParser = null;
    
    @Override
    public CricketMatch parseMatch(StringBuilder sb) throws Exception {
        if (sb == null || sb.length() == 0) throw new Exception("Initial data source is empty!");
        CricketMatch match = new CricketMatch();
        
        int pos = sb.indexOf("<div class=\"headLeftDiv\">");
        if (pos < 0) throw new Exception("Can't find left part of the header!... ['<div class=\"headLeftDiv\">']" );
        int epos = sb.indexOf("</div>", pos + 5);
        
        Plogger.printSubHeader("Analyzing left header...");
        this.analyzeHeaderLeft(match, sb.substring(pos, epos));
        Plogger.printSubHeader("Completed analyzing left header.");
        
        pos = sb.indexOf("<div class=\"headRightDiv\">", pos + 10);
        if (pos < 0) throw new Exception("Can't find right part of the header!... ['<div class=\"headRightDiv\">']" );
        epos = sb.indexOf("</div>", pos + 5);
        
        Plogger.printSubHeader("Analyzing right header...");
        this.analyzeHeaderRight(match, sb.substring(pos, epos));
        Plogger.printSubHeader("Completed analyzing right header.");
        
        Plogger.printSubHeader("Analyzing innings...");
        ArrayList<SingleInning> allInns = new ArrayList<SingleInning>();
        String[] tables = MyStringUtils.splitInBetween(sb.toString(), "<table", "</table>");
        StringBuilder singleInn = null;
        StringBuilder matchNotes = new StringBuilder();
        int inncount = 0;
        for (int i = 0; i < tables.length; i++) {
            String table = "<table" + tables[i];
            
            pos = table.indexOf('>');
            if (pos < 0) continue;
            HashMap<String, String> attribs = HTMLStringUtils.getHTMLAttributes(table.substring(0, pos + 1));
            if (attribs != null) {
                String tmp = attribs.get("id");
                
                if (tmp != null) {  // found inning details
                    if (tmp.startsWith("inningsBat")) { // found a batting inning
                        
                        singleInn = new StringBuilder();
                        singleInn.append(table + "</table>");
                        
                    } else if (tmp.startsWith("inningsBowl")) { // found a bowling inning
                        singleInn.append(table + "</table>");
                        
                        if (this.singleInnParser != null) {
                            
                            Plogger.debug("analyzing an inning (#" + (inncount + 1) + ")...");
                            SingleInning tmpInn = this.singleInnParser.parseSingleInning(singleInn);
                            Plogger.debug("  completed analyzing an inning...");
                            if (tmpInn != null) {
                                inncount++;
                                tmpInn.setInningMatch(inncount);
                                allInns.add(tmpInn);
                            } else {
                                Plogger.warn("  empty inning is found!");
                            }
                            
                        } else {
                            Plogger.printHardWarn("No SingleInningParsable interface is defined!");
                        }
                        
                        singleInn = null;
                    } 
                    
                } else {
                    
                    tmp = attribs.get("class");
                    if (tmp != null) {
                        if (tmp.startsWith("inningsTable")) { // found a sub inning table
                        	if (singleInn == null) singleInn = new StringBuilder();
                            singleInn.append(table + "</table>");
                        }
                    }
                    
                }
                
                tmp = attribs.get("class");
                if (tmp != null) {
                    if (tmp.startsWith("notesTable")) { // found a match note
                    	
                    	if (singleInn != null) {
                    		Plogger.debug("found an inning which not parsed without a bowling list!");
                    		
                    		singleInn.append("</table>");
                            
                            if (this.singleInnParser != null) {
                                
                                Plogger.debug("analyzing an inning (#" + (inncount + 1) + ")...");
                                SingleInning tmpInn = this.singleInnParser.parseSingleInning(singleInn);
                                Plogger.debug("  completed analyzing an inning...");
                                if (tmpInn != null) {
                                    inncount++;
                                    tmpInn.setInningMatch(inncount);
                                    allInns.add(tmpInn);
                                } else {
                                    Plogger.warn("  empty inning is found!");
                                }
                                
                            } else {
                                Plogger.printHardWarn("No SingleInningParsable interface is defined!");
                            }
                            
                            singleInn = null;
                    	}
                    	
                        Plogger.debug("parsing match notes...");
                        matchNotes = new StringBuilder();
                        matchNotes.append(table);
                        Plogger.debug("  completed parsing match notes...");
                        break;
                    }
                }
                
            }
            
        }
        Plogger.debug("found innings: " + allInns.size());
        SingleInning[] innings = new SingleInning[allInns.size()];
        allInns.toArray(innings);
        match.setInnings(innings);
        Plogger.printSubHeader("Completed parsing innings.");
        
        Plogger.printSubHeader("Parsing match footer...");
        String[] rows = MyStringUtils.splitInBetween(matchNotes.toString(), "<tr ", "</tr>");
        int count = 0;
        if (rows != null) {
            
            for (int i=0;i<rows.length;i++) {
                String row = "<tr " + rows[i];
                
                pos = row.indexOf('>');
                if (pos < 0) continue;
                HashMap<String, String> attribs = HTMLStringUtils.getHTMLAttributes(row.substring(0, pos + 1));
                if (attribs != null) {
                    String tmp = attribs.get("class");
                    
                    if (tmp != null) {
                        if (tmp.equalsIgnoreCase("notesRow")) {
                            count++;
                            
                            if (count == 1) {
                                
                                Plogger.debug("parsing toss and series result...");
                                this.analyzeFooterTossAndSeries(match, row);
                                Plogger.debug("  Completed parsing toss and series result...");
                                
                            } else if (count == 2) {
                                
                                Plogger.debug("parsing man of the match/series and debuts...");
                                this.analyzeFooterMatchPlayerDetails(match, row);
                                Plogger.debug("  Completed parsing man of the match/series and debuts...");
                                
                            } else if (count == 3) {
                                
                                if (this.officialParser != null) {
                                    Plogger.debug("parsing officials...");
                                    match.setOfficials(this.officialParser.parseOfficials(row, match));
                                    Plogger.debug("  Completed parsing officials...");
                                } else {
                                    Plogger.printHardWarn("No MatchOfficialParsable interface is defined!");
                                }
                                
                            }
                            
                        }
                    }
                    
                }
                
            }
            
        } else {
            Plogger.warn("No Match footer data rows are found!");
        }
        
        Plogger.printSubHeader("Completed parsing match footer.");
        
        return match;
    }

    private void analyzeFooterMatchPlayerDetails(CricketMatch match, String source) throws Exception {
        if (source == null || source.length() == 0) return;
        
        String coldata = MyStringUtils.inBetween(source, "<td>", "</td>");
        coldata = coldata.replace("\n", "");
        
        String[] rows = coldata.split("<br>");
        for (int i=0; i<rows.length; i++) {
            String title = MyStringUtils.inBetween(rows[i], "<b>", "</b>");
            if (title == null) continue;
            int pos = rows[i].indexOf("</b>");
            
            if (title.toLowerCase().indexOf("of the match") >= 0) {
                
                if (this.simpleNameParser != null) {
                    Plogger.debug("  man of the match info found. parsing...");
                    match.setManOfTheMatch(this.simpleNameParser.parserNames(rows[i].substring(pos + 4)));
                    Plogger.debug("  ok.");
                } else {
                    Plogger.printHardWarn("No SimpleNameParsable interface is defined!");
                }
                
            } else if (title.toLowerCase().indexOf("of the series") >= 0) {
                
                if (this.simpleNameParser != null) {
                    Plogger.debug("  man of the series info found. parsing...");
                    match.setManOfTheSeries(this.simpleNameParser.parserNames(rows[i].substring(pos + 4)));
                    Plogger.debug("  ok.");
                } else {
                    Plogger.printHardWarn("No SimpleNameParsable interface is defined!");
                }
                
            } else if (title.toLowerCase().indexOf(" debut") >= 0) {
                
                if (this.debutParser != null) {
                    Plogger.debug("  debutants info found. parsing...");
                    match.setDebutPlayers(this.debutParser.parseDebutants(new StringBuilder(rows[i].substring(pos + 4)))); 
                    Plogger.debug("  ok.");
                } else {
                    Plogger.printHardWarn("No DebutsParsable interface is defined!");
                }
                
            }
            
        }
        
    }
    
    private void analyzeFooterTossAndSeries(CricketMatch match, String source) throws Exception {
        if (source == null || source.length() == 0) return;
        
        
        String coldata = MyStringUtils.inBetween(source, "<td>", "</td>");
        coldata = coldata.replace("\n", "");

        String[] rows = coldata.split("<br>");
        if (rows.length >= 2) {
            
            if (this.tossParser != null) {
                Plogger.debug("  toss info found. parsing...");
                match.setToss(this.tossParser.parseToss(rows[0], match));
                Plogger.debug("  ok.");
            } else {
                Plogger.printHardWarn("No TossParsable interface is defined!");
            }
            
            if (this.seriesResultParser != null) {
                Plogger.debug("  series result found. parsing...");
                this.seriesResultParser.parseSeriesResult(rows[1], match);
                Plogger.debug("  ok.");
            } else {
                Plogger.printHardWarn("No SeriesResultParsable interface is defined!");
            }
            
        } else {
            Plogger.warn("  No toss or series result is found!");
        }
    }
    
    private void analyzeHeaderRight(CricketMatch match, String source) throws Exception {
        if (source.length() == 0) return;
        
        String[] ptags = MyStringUtils.splitInBetween(source, "<li>", "</li>");
        if (ptags.length == 3) {
            
            String[] idAndSeason = MyStringUtils.splitInBetween(ptags[0], "<a href", "</a>");
            if (idAndSeason.length >= 2) {
                
                if (this.idParser != null) {
                    
                    Plogger.debug("started match id parsing...");
                    match.setId(this.idParser.parseMatchID(HTMLStringUtils.cleanHTMLTags("<" + idAndSeason[0]), match));
                    Plogger.debug("  completed match id parsing.");
                
                } else {
                    Plogger.printHardWarn("No MatchIDParsable interface is defined!");
                }
                
                if (match.getSeries() != null) {
                    Plogger.debug("started season parsing...");
                    String tmpSeason = HTMLStringUtils.cleanHTMLTags("<" + idAndSeason[1]);
                    int pos = tmpSeason.indexOf(' ');
                    if (pos > 0) {
                        match.getSeries().setSeason(tmpSeason.substring(0, pos).trim());
                    }
                    Plogger.debug("  ok.");
                } else {
                    Plogger.printHardWarn("No SeriesPlayed has been initialized!");
                    Plogger.printHardWarn("First MatchSeriesParsable interface must be run!");
                }
                
                
            } else {
                
                Plogger.warn("missing: match id & season details!");
                
            }
            
            if (this.groundParser != null) {
                Plogger.debug("started ground parsing...");
                match.setGround(this.groundParser.parseGround(ptags[1], match));
                Plogger.debug("  completed ground parsing.");
            } else {
                Plogger.printHardWarn("No GroundParsable interface is defined!");
            }
            
            if (this.dateParser != null) {
                Plogger.debug("started dates parsing...");
                match.setDates(this.dateParser.parseMatchDates(ptags[2], match));
                Plogger.debug("  completed dates parsing.");
            } else {
                Plogger.printHardWarn("No MatchDatesParsable interface is defined!");
            }
            
            
        } else {
            throw new Exception("Right side of Header has " + ptags.length + " parts! Number of parts must be 3!");
        }
    }
    
    private void analyzeHeaderLeft(CricketMatch match, String source) throws Exception {
        if (source.length() == 0) return;
        
        String[] ptags = MyStringUtils.splitInBetween(source, "<p class", "</p>");
        if (ptags.length == 3) {
            
            if (this.seriesParser != null) {
                
                Plogger.debug("parsing series...");
                ptags[0] = ptags[0].substring(ptags[0].indexOf('>') + 1);
                match.setSeries(this.seriesParser.parseSeries(ptags[0], match));
                Plogger.debug("ok.");
                
            } else {
                Plogger.printHardWarn("No MatchSeriesParsable interface is defined!");
            }
            
            
            
            String[] teams = MyStringUtils.splitInBetween(ptags[1], "<a href", "</a>");
            if (teams.length >= 2) {
                match.setTeam1(HTMLStringUtils.cleanHTMLTags("<" + teams[0]));
                match.setTeam2(HTMLStringUtils.cleanHTMLTags("<" + teams[1]));
                
                Plogger.info("   teams: " + match.getTeam1() + " v " + match.getTeam2());
                Plogger.debug("completed teams parsing.");
            } else {
                Plogger.warn("Number of teams must be 2!");
            }
            
            if (this.resultParser != null) {
                
                Plogger.debug("parsing results...");
                String tmpRes = HTMLStringUtils.cleanHTMLTags("<" + ptags[2]);
                match.setResult(this.resultParser.parseMatchResult(tmpRes, match));
                Plogger.debug("ok.");
            
            } else {
                Plogger.printHardWarn("No MatchResultParsable interface is defined!");
            }
            
        } else {
            throw new Exception("Left side of Header has " + ptags.length + " parts! Number of parts must be 3!");
        }
    }
    
    public GroundParsable getGroundParser() {
        return groundParser;
    }

    public void setGroundParser(GroundParsable groundParser) {
        this.groundParser = groundParser;
    }

    public MatchDatesParsable getDateParser() {
        return dateParser;
    }

    public void setDateParser(MatchDatesParsable dateParser) {
        this.dateParser = dateParser;
    }

    public MatchIDParsable getIdParser() {
        return idParser;
    }

    public void setIdParser(MatchIDParsable idParser) {
        this.idParser = idParser;
    }

    public MatchResultParsable getResultParser() {
        return resultParser;
    }

    public void setResultParser(MatchResultParsable resultParser) {
        this.resultParser = resultParser;
    }

    public void setSpanParser(MatchSpanParsable spanParser) {
        this.spanParser = spanParser;
    }

    public MatchSpanParsable getSpanParser() {
        return spanParser;
    }
    
    public void setDebutParser(DebutsParsable debutParser) {
        this.debutParser = debutParser;
    }
    
    public DebutsParsable getDebutParser() {
        return debutParser;
    }
    
    public void setSingleInnParser(SingleInningParsable singleInnParser) {
        this.singleInnParser = singleInnParser;
    }
    
    public SingleInningParsable getSingleInnParser() {
        return singleInnParser;
    }
    
    public void setSeriesParser(MatchSeriesParsable seriesParser) {
        this.seriesParser = seriesParser;
    }
    
    public MatchSeriesParsable getSeriesParser() {
        return seriesParser;
    }
    
    public void setTossParser(TossParsable tossParser) {
        this.tossParser = tossParser;
    }
    
    public TossParsable getTossParser() {
        return tossParser;
    }
    
    public void setSeriesResultParser(SeriesResultParsable seriesResultParser) {
        this.seriesResultParser = seriesResultParser;
    }
    
    public SeriesResultParsable getSeriesResultParser() {
        return seriesResultParser;
    }
    
    public void setSimpleNameParser(SimpleNameParsable simpleNameParser) {
        this.simpleNameParser = simpleNameParser;
    }
    
    public SimpleNameParsable getSimpleNameParser() {
        return simpleNameParser;
    }
    
    public void setOfficialParser(MatchOfficialParsable officialParser) {
        this.officialParser = officialParser;
    }
    
    public MatchOfficialParsable getOfficialParser() {
        return officialParser;
    }
}
